const GameLogic = require('./gameLogic');
const AiLogic = require('./aiLogic');
const utils = require('../../util/utils');
const gameProto = require('./gameProto');
const roomProto = require('../roomFrameProto');
const logger = require('pomelo-logger').getLogger('pdk');
const moment = require('moment');
const roomBaseFrame = require('../roomBaseFrame');
const async = require('async');
const enumeration = require('../../constant/enumeration');

class gameFrame extends roomBaseFrame {


	constructor(roomID, creatorInfo, gameRule) {

		super(roomID, creatorInfo, gameRule);

		this.baseScore = 1;
		this.gameLogic = new GameLogic(gameRule);
		this.aiLogic = new AiLogic(gameRule);
		this.handCards = {}; //玩家手牌
		this.cloneHandCards = {};
		this.leftCards = null;
		this.gameStatus = gameProto.gameStatus.NONE; //等待状态
		this.result = null; //游戏结果
		this.bankerUid = null;
		this.winList = {}; //玩家所有输赢
		this.heiHong3User = null;
		this.hongtao10User = null; //红桃10翻倍玩家
		this.heitao3Code = 51;
		this.hongtao3Code = 35;
		this.hongtao10Code = 42;
		this.trustTm = gameRule.trustTm || 0; //0 不托管 其余为超时托管
		this.autoUserTimer = {};
		this.autoUser = {};
		this.autoTimeout = 1;
		//炸弹设置
		this.curBoomUser = null;  //出了炸弹的玩家
		this.boomScore = gameRule.boomScore || 10; //炸弹分默认10
		this.boomType = 2;  //1表示都算分 2：表示只有
		this.washGold = 5;
		this.userWashList = [];


		//其他设置
		this.cardCount = gameRule.cardCount || 16; //经典玩法16张 和15张玩法
		this.fourTakeTwo = gameRule.fourTakeTwo || false; //是否可以4带2
		this.fourTakeThree = gameRule.fourTakeThree || false; //是否可以4带3
		this.a3Boom = gameRule.a3Boom || false; //3A算炸弹
		this.chaizhadan = gameRule.chaizhadan || false; //默认不可拆炸弹 true 可以拆炸弹
		//this.heitao3 = gameRule.heitao3 || false; //true 黑桃3是否必须出牌 
		this.bichu = gameRule.bichu || false; //true 必出 false 可不出 
		this.hongtao10 = gameRule.hongtao10 || false; //红桃10双倍
		this.last10 = gameRule.last10 || false; //剩余10张1.5倍，true为1.5倍，false为不翻
		this.isQiangGuan = gameRule.isQiangGuan || false; //是否抢关
		this.outCardSort = gameRule.outCardSort || 1; //1：黑桃3 2：红桃3 3：上轮先跑 4：轮流先出
		if ([gameProto.outSortType.WIN, gameProto.outSortType.TURN].includes(this.outCardSort)) {
			this.isQiangGuan = false;
		}
		this.isDouble = gameRule.isDouble || false; //是否加倍
		this.isFanGuan = gameRule.isFanGuan || false; //是否反关
		this.isShuaiWei = gameRule.isShuaiWei || false; //是否甩尾
		this.baoDanBiDa = gameRule.baoDanBiDa || false; //报单必大

		//操作玩家
		this.totalTick = 0; //当前阶段总时间
		this.tick = 0; //倒计时时间
		this.curOperateUid = null; //当前操作玩家
		this.lastOperateUid = null; //最后一个出牌的玩家
		this.lastOperateCards = null; //最后操作的卡牌
		this.curOperateCards = null; //当前操作卡牌
		this.operateTimer = null;
		this.endOperateTime = null; //倒计时定时器
		this.operateRecords = [];
		this.baoDanUser = []; //报单玩家
		this.baoDanPeiUser = null; //报单赔玩家
		this.baoShuangUser = []; //报双玩家
		this.userBoomScore = {}; //玩家炸弹分
		this.qiangGuanUser = null;//抢关玩家 
		this.curQiangUser = null; //当前抢关玩家
		this.qiangUserOperate = {};//玩家操作是否枪关
		this.doubleUser = {};  //玩家是否选择double
		//玩家测试卡牌
		this.userTestCards = {}; //玩家测试卡牌
		this.userNextTestCards = {}; //下一局玩家测试卡牌

		this.winCount = {};
		this.danGuanCount = {};
		this.shuangGuanCount = {}
		this.beiGuanCount = {};
		this.fanGuanCount = {};

	}

	//游戏获取玩家发送的消息
	receiveGameMessage(uid, msg) {

		let type = msg.type || null;
		let data = msg.data || null;
		if (!type || !data) return;

		if (type === gameProto.GAME_REVIEW_NOTIFY) {
			//牌局回放
			this.onGameReview(uid);
		} else if (type === gameProto.GAME_USER_OPERATE_NOTIFY) {
			//玩家操作
			this.onUserOperate(uid, data, true);

		} else if (type == gameProto.GAME_SET_TEST_CARDS_NOTIFY) {
			//玩家换牌
			this.onChangeCard(uid, data);

		} else if (type == gameProto.GAME_GET_TEST_CARDS_NOTIFY) {
			//玩家查询牌库
			this.onSearchCardsList(uid, data);
		} else if (type === gameProto.GAME_TRUST_NOTIFY) {
			//玩家托管
			this.onGameTrust(uid, data);
		} else if (type === gameProto.GAME_QIANG_GUAN_NOTIFY) {
			//玩家抢关
			this.onUserQiangGuan(uid, data);
		} else if (type === gameProto.GAME_USER_DOUBLUE_NOTIFY) {
			//玩家加倍
			this.onUserDouble(uid, data);
		} else if (type === gameProto.GAME_USER_CHOOSE_WASH_NOTIFY) {
			//结束选择洗牌
			this.userChooserWash(uid);
		}
	};


	//重置游戏状态
	resetGame() {

		this.clearOperateTimer();
		//重置游戏状态
		this.gameStatus = gameProto.gameStatus.NONE;
		this.sendGameStatus(this.gameStatus);
		let userList = this.getPlayingUserList();
		this.result = null;

		this.lastOperateUid = null; //最后一个出牌的玩家
		this.lastOperateCards = null; //最后操作的卡牌
		this.curOperateCards = null; //当前操作卡牌
		this.curQiangUser = null; //当前抢关玩家
		this.operateTimer = null;
		this.endOperateTime = null; //倒计时定时器
		this.operateRecords = [];
		this.heiHong3User = null; //当前出牌玩家
		this.hongtao10User = null; //红桃10翻倍
		this.baoDanUser = []; //报单玩家
		this.baoShuangUser = []; //报双玩家
		this.doubleUser = {};  //加倍玩家
		this.qiangGuanUser = null;//抢关玩家 
		this.qiangUserOperate = {};//玩家操作是否枪关
		this.curBoomUser = null;  //出了炸弹的玩家
		this.baoDanPeiUser = null; //报单赔玩家
		this.userWashList = [];
		//初始化玩家基础数据
		this.userTestCards = this.cloneObject(this.userNextTestCards);
		this.userNextTestCards = {};
		for (let i = 0; i < userList.length; i++) {
			let uid = userList[i];
			this.handCards[uid] = []; //玩家手牌
			this.cloneHandCards[uid] = [];
			this.userBoomScore[uid] = 0;
		}

	};


	getEnterGameData(uid) {

		let curTick = (this.endOperateTime || moment()).diff(moment(), 's', true);
		let sendData = {
			gameStatus: this.gameStatus,
			gameStarted: this.gameStarted,
			handCards: this.getHandCardsByUid(uid),
			curBureau: this.curBureau,
			operateRecords: this.operateRecords,
			tick: curTick < 0 ? 0 : curTick,
			userWinTotalScore: this.userWinTotalScore,
			lastOperateCards: this.lastOperateCards,
			lastOperateUid: this.lastOperateUid,
			gameRule: this.gameRule,
			leftCards: this.getAllLeftCards(uid),
			totalTick: this.totalTick,
			washGold: this.washGold,
			doubleUser: this.doubleUser,
			curQiangUser: this.curQiangUser,
			qiangUserOperate: this.qiangUserOperate,
			qiangGuanUser: this.qiangGuanUser,
			maxBureau: this.maxBureau,
			autoUser: this.autoUser,
			chairCount: this.chairCount,
			curOperateUid: this.curOperateUid,
			userWashList: this.userWashList,
			userPlayStatus: this.getAllUserPlayStatus(),
			result: null,
		};
		return sendData;
	};


	//根据uid或者可以看到的手牌
	getHandCardsByUid(uid) {

		let curCloneCards = {};
		let curUser = this.getUserByUID(uid);
		if (curUser && curUser.userInfo.isTest) {
			//调试权限可以看所有牌
			return this.handCards;
		} else {
			for (let key in this.handCards) {
				let curCards = this.handCards[key].slice();
				curCloneCards[key] = key == uid ? curCards : curCards.fill(0)
			}
			return curCloneCards;
		}
	}

	//自动
	async onGameTrust(uid, data) {
		this.autoUser[uid] = data.trust;
		await this.sendGameData(gameProto.gameTrushPush(uid, data.trust), [uid]);
		if (data.trust) {
			this.userAutoOperate(uid);
		}
		if (this.autoUserTimer[uid]) {
			clearTimeout(this.autoUserTimer[uid]);
			delete this.autoUserTimer[uid];
		}
	};

	//游戏开始
	async onEventGameStart() {

		this.resetGame();
		this.clearOperateTimer();
		//随机出牌
		if (!this.bankerUid) {
			this.bankerUid = this.getDefaultBanker();
		}

		//局数推送
		this.curBureau += 1;
		await this.sendGameData(gameProto.gameBureauPush(this.curBureau));

		this.gameStarted = true;
		this.showLog(`[pdkkkk] game start ...`);

		this.startSendCards();
	};



	//用户选择切牌
	async userChooserWash(uid) {

		if (this.gameStatus != gameProto.gameStatus.NONE) return;
		//判断钻石是否足够
		let baseGold = 0;
		if (this.gameRule.payType == enumeration.roomPayType.AAZHIFU) {
			baseGold = this.maxBureau - this.curBureau;
		} else if (this.gameRule.payType == enumeration.roomPayType.YINGJIAZHIFU) {
			baseGold = this.getRoomPayDiamond();
		}
		let curUser = await this.userPayWash(uid, baseGold, this.washGold);
		if (curUser) {
			this.userWashList.push(uid);
			this.sendGameData(gameProto.userChooseWashResult(uid, this.userWashList, true, '洗牌成功'));
			setTimeout(() => {
				this.userReady(uid);
			}, 2500)

		} else {
			this.sendGameData(gameProto.userChooseWashResult(uid, this.userWashList, false, '钻石不足'), [uid]);
		}
	}

	//发牌
	async startSendCards() {

		//更新游戏状态到发牌阶段
		this.gameStatus = gameProto.gameStatus.SEND_CARDS;
		this.totalTick = gameProto.gameStatusTm.SEND_CARDS;
		this.sendGameStatus(this.gameStatus, this.totalTick);

		// let ISuserTiao = false;
		// let heimingdan = []; //黑名单
		// let baimingdan = []; //白名单
		// let userList = this.getPlayingUserList();
		// for (let i = 0; i < userList.length; i++) {
		// 	let uid = userList[i];
		// 	if (this.userTestCards[uid] && this.userTestCards[uid].length > 0) {
		// 		ISuserTiao = true;
		// 	}
		// 	let user = this.getUserByUID(uid);
		// 	if (user && user.userInfo.userType == 1) {
		// 		heimingdan.push(uid);
		// 	} else if (user && user.userInfo.userType == 2) {
		// 		baimingdan.push(uid);
		// 	}
		// }
		// if (!ISuserTiao && (heimingdan.length > 0 || baimingdan.length > 0) && Math.random() < 0.3) {
		// 	for (let i = 0; i < userList.length; i++) {
		// 		let uid = userList[i];
		// 		if (heimingdan.includes(uid)) {
		// 			this.userTestCards[uid] = this.aiLogic.userReSortCard(this.userTestCards, false);
		// 		} else if (baimingdan.includes(uid)) {
		// 			this.userTestCards[uid] = this.aiLogic.userReSortCard(this.userTestCards, true);
		// 		} else {
		// 			if (heimingdan.length > 0) {
		// 				this.userTestCards[uid] = this.aiLogic.userReSortCard(this.userTestCards, true);
		// 			} else {
		// 				this.userTestCards[uid] = this.aiLogic.userReSortCard(this.userTestCards, false);
		// 			}
		// 		}
		// 	}
		// }

		this.userTestCards = this.clearTestCards(this.userTestCards);
		let allCards = this.gameLogic.getWashedCards(this.userTestCards);

		//玩家开始发牌
		let userList = this.getPlayingUserList();
		let cardCount = parseInt(this.gameRule.cardCount);
		for (let i = 0; i < userList.length; i++) {
			let uid = userList[i];
			let cards = null;
			//张牌
			if (this.userTestCards[uid]) {
				cards = this.userTestCards[uid];
				let leftCount = cardCount - cards.length;
				if (leftCount > 0) {
					let otherCard = allCards.splice(0, leftCount);
					cards = cards.concat(otherCard);
				}
				cards.sort((a, b) => { return this.gameLogic.getCardLogicValue(b) - this.gameLogic.getCardLogicValue(a) });
				this.showLog(`[${uid}] user get test cards = ${JSON.stringify(cards)}`);
			} else {
				cards = allCards.splice(0, cardCount);
				cards.sort((a, b) => { return this.gameLogic.getCardLogicValue(b) - this.gameLogic.getCardLogicValue(a) });
				this.showLog(`[${uid}] user get cards = ${JSON.stringify(cards)}`);
			}

			//判断该谁出牌
			if (this.outCardSort == gameProto.outSortType.HEI3 && cards.includes(this.heitao3Code)) {
				this.heiHong3User = uid;
				this.showLog(`[${uid}] heitao3 user first code = ${this.heitao3Code}`);
			}

			if (this.outCardSort == gameProto.outSortType.HONG3 && cards.includes(this.hongtao3Code)) {
				this.heiHong3User = uid;
				this.showLog(`[${uid}] hongtao3 user first code = ${this.hongtao3Code}`);
			}

			//是否红桃10翻倍
			if (this.hongtao10 && cards.includes(this.hongtao10Code)) {
				this.hongtao10User = uid;
				this.showLog(`[${uid}] hongtao10 double = ${this.hongtao10} code = ${this.hongtao10Code} `);
			}

			this.handCards[uid] = cards;
			this.cloneHandCards[uid] = cards;
			//发牌记录
			let data = { cards: cards, uid: uid, type: gameProto.operateType.SEND };
			this.operateRecords.push(data);
		}


		//玩家如果没有指定牌 需要切换
		if ([gameProto.outSortType.HONG3, gameProto.outSortType.HEI3].includes(this.outCardSort) && !this.heiHong3User) {

			let randUid = this.getDefaultBanker();
			this.heiHong3User = randUid;
			let curIndex = this.handCards[randUid].findIndex(item => item == this.gameLogic.getCardLogicValue(item) == 3);
			if (curIndex == -1) {
				curIndex = this.handCards[randUid].length - 1;
			}
			let curCard = this.handCards[randUid].splice(curIndex, 1);
			let cur3Card = this.outCardSort == gameProto.outSortType.HONG3 ? this.hongtao3Code : this.heitao3Code;
			let changeIndex = allCards.findIndex(item => item == cur3Card);
			let curEndCard = allCards.splice(changeIndex, 1);
			allCards.push(curCard[0]);
			this.handCards[randUid].push(curEndCard[0]);
			this.handCards[randUid].sort((a, b) => { return this.gameLogic.getCardLogicValue(b) - this.gameLogic.getCardLogicValue(a) });

			this.showLog(`[pdkkk] change start card randUid = ${randUid} userCard = ${curCard[0]} changeCard = ${curEndCard[0]} `);
		}


		//剩余卡牌
		allCards.sort((a, b) => { return this.gameLogic.getCardLogicValue(b) - this.gameLogic.getCardLogicValue(a) });
		this.leftCards = allCards;
		this.showLog(`[pdkkkk] leftCards = ${JSON.stringify(this.leftCards)}`);

		//推送发牌消息
		for (let i = 0; i < userList.length; i++) {
			let uid = userList[i];
			let curCards = this.getHandCardsByUid(uid);
			this.showLog(`[${uid}] send user cards = ${JSON.stringify(this.handCards[uid])} `);
			await this.sendGameData(gameProto.gameSendCardPush(curCards), [uid]);
		}

		this.bankerUid = this.heiHong3User ? this.heiHong3User : this.bankerUid;

		setTimeout(() => {

			if (this.isQiangGuan) {
				//开始抢关
				this.totalTick = gameProto.gameStatusTm.USER_QIANG;
				this.gameStatus = gameProto.gameStatus.USER_QIANG;
				this.sendGameStatus(this.gameStatus, this.totalTick);
				//推送玩家抢关
				this.startQiangGuan(this.bankerUid);

			} else if (this.isDouble) {
				//玩家加倍
				this.startUserDouble();
			} else {
				//开始游戏
				this.startGameBegin();
			}
		}, this.totalTick * 1000);

	};


	//正式开始游戏
	startGameBegin() {

		//玩家开始操作
		this.gameStatus = gameProto.gameStatus.USER_OPERATE;
		this.totalTick = gameProto.gameStatusTm.USER_OPERATE;
		this.sendGameStatus(this.gameStatus, this.totalTick);
		//开始玩家操作
		this.curOperateUid = this.bankerUid;
		this.startUserOperate(this.curOperateUid);
	}

	startQiangGuan(uid) {

		this.showLog(`[${uid}] start qiang guan`);
		//清除定时器
		this.clearOperateTimer();
		this.curQiangUser = uid;
		this.curOperateUid = uid;
		if (this.gameStatus == gameProto.gameStatus.USER_QIANG) {

			this.totalTick = gameProto.gameStatusTm.USER_QIANG;
			this.sendGameData(gameProto.gameStartQiangGuanPush(uid, this.totalTick));

			this.checkUserAuto(uid);
			//超时不抢关
			this.operateTimer = setTimeout(() => {
				if (this.gameStatus == gameProto.gameStatus.USER_QIANG) {
					//默认不抢关
					this.onUserQiangGuan(uid, { isQiang: false });
				}
			}, this.totalTick * 1000);
			this.endOperateTime = moment().add(this.totalTick, 's');
		}

	};


	//玩家加倍
	onUserDouble(uid, data) {

		this.showLog(`[${uid}] on user double data = ${JSON.stringify(data)} `);
		if (this.gameStatus == gameProto.gameStatus.USER_DOUBLE) {

			let isDouble = data.isDouble;
			this.doubleUser[uid] = isDouble;
			this.sendGameData(gameProto.gameUserDoublePush(uid, isDouble));
			if (utils.getLength(this.doubleUser) == this.chairCount) {
				this.endUserDoublue();
			}
		}

	}

	//玩家抢关
	onUserQiangGuan(uid, data) {

		this.showLog(`[${uid}] on user qiang guan data = ${JSON.stringify(data)} `);
		if (this.curQiangUser == uid && this.gameStatus == gameProto.gameStatus.USER_QIANG) {


			this.qiangUserOperate[uid] = data.isQiang;

			this.clearOperateTimer();
			if (data.isQiang) {
				//玩家抢关
				this.qiangGuanUser = uid; //抢关玩家
				this.bankerUid = uid; //抢关玩家先出
				setTimeout(() => {
					if (this.isDouble) {
						this.startUserDouble();
					} else {
						this.startGameBegin();
					}
				}, 1000);
			}
			this.sendGameData(gameProto.gameQiangGuanPush(uid, data.isQiang, this.qiangGuanUser));
			//如果玩家不抢呢
			if (!data.isQiang) {

				let nextQiangUser = this.getNextUserID(uid);
				if (nextQiangUser && nextQiangUser != this.bankerUid) {
					//玩家开始抢关
					this.startQiangGuan(nextQiangUser);
				} else {
					setTimeout(() => {
						if (this.isDouble) {
							this.startUserDouble();
						} else {
							//开始游戏
							this.startGameBegin();
						}
					}, 1000);
				}
			}
		}
	}


	//群发玩家加倍
	startUserDouble() {


		this.showLog(`[pdkkkk] game start user double `);

		this.gameStatus = gameProto.gameStatus.USER_DOUBLE;
		this.totalTick = gameProto.gameStatusTm.USER_DOUBLE;
		this.sendGameStatus(this.gameStatus, this.totalTick);

		let userList = this.getPlayingUserList();
		for (let i = 0; i < userList.length; i++) {
			this.checkUserAuto(userList[i]);
		}

		//开始玩家操作
		this.operateTimer = setTimeout(() => {
			//结束抢关
			if (this.gameStatus == gameProto.gameStatus.USER_DOUBLE) {
				this.endUserDoublue();
			}
		}, this.totalTick * 1000);

		this.endOperateTime = moment().add(this.totalTick, 's');
	}


	//游戏结束加倍
	endUserDoublue() {

		this.showLog(`[pdkkkk] game end user double `);
		this.clearOperateTimer();

		setTimeout(() => {
			this.startGameBegin();
		}, 1000);

	}

	//超时处理
	checkUserAuto(uid) {
		let user = this.getUserByUID(uid);
		if (user && (user.userInfo.isRobot || this.autoUser[uid])) {
			let timeOut = user.userInfo.isRobot ? utils.getRandomNum(2, 4) : this.autoTimeout;
			setTimeout(() => { this.userAutoOperate(uid); }, timeOut * 1000);
		}
	}



	//检测是否全部出完
	checkUserOutAll(uid) {

		let curCards = this.handCards[uid].slice();
		if (this.lastOperateCards) {

			let cardType = this.gameLogic.getCardType(curCards.slice(), this.isShuaiWei);
			//最后一首如果是三代二 甩尾的时候特殊判断
			let compareResult = this.gameLogic.compareCard(curCards.slice(), this.lastOperateCards, this.isShuaiWei);
			if (compareResult.isSuccess && cardType != gameProto.cardType.FOUR_LINE_TAKE_X) {
				return true;
			} else {
				return false;
			}

		} else {
			//最后一手
			let aCount = curCards.filter(item => this.gameLogic.getCardLogicValue(item) == 14);
			if (this.a3Boom && aCount.length >= 3 && curCards.length <= 5 && curCards.length > 3) {
				//判断是否3带A的炸弹
				this.showLog(`[${uid}] a3boom can not last out cards = ${JSON.stringify(curCards)} a3Boom = ${this.a3Boom} `);
				return false;
			}
			let cardType = this.gameLogic.getCardType(curCards.slice(), this.isShuaiWei);
			if (cardType && cardType != cardType != gameProto.cardType.FOUR_LINE_TAKE_X) {
				return true;
			} else {
				return false;
			}
		}
	}


	//给用户推送操作
	startUserOperate(uid) {

		this.showLog(`[${uid}] user turn start operate `);
		//玩家操作后清除定时器
		this.clearOperateTimer();

		//判断玩家是否可以出牌
		if (!this.handCards || !this.handCards[uid]) { return; }

		//判断是否出完
		if (this.checkUserOutAll(uid)) {
			//可以一次出完
			let data = { cards: this.handCards[uid].slice(), operateType: gameProto.operateType.OUT, isShuaiWei: true };
			this.onUserOperate(uid, data);
			this.showLog(`[${uid}] auto last out cards = ${JSON.stringify(data.cards)}`);
			return;
		}
		else {

			let canOutCard = this.gameLogic.searchOutCard(this.handCards[uid].slice(), this.lastOperateCards);
			if (canOutCard.length == 0) {
				//要不起 自动过
				let data = { cards: null, operateType: gameProto.operateType.PASS };
				setTimeout(() => {
					this.onUserOperate(uid, data);
					this.showLog(`[${uid}] auto pass user turn`);
				}, 1000)
				return;
			}

			//推送玩家操作信息
			this.totalTick = this.trustTm ? this.trustTm : gameProto.gameStatusTm.USER_OPERATE;
			let operateMessage = gameProto.gameUserOperateStartPush(uid, this.lastOperateCards, this.lastOperateUid, this.totalTick);
			this.sendGameData(operateMessage);
			let user = this.getUserByUID(uid);
			if (this.autoUser[uid] || user && user.userInfo.isRobot) {
				setTimeout(() => {
					this.userAutoOperate(uid);
				}, 2000);
			} else {

				//有超时托管
				if (this.trustTm) {
					//如果没有操作 则记录超时托管
					this.autoUserTimer[uid] = setTimeout(() => {
						//玩家托管
						this.showLog(`[${uid}] user timeout auto begin `);
						this.onGameTrust(uid, { trust: true });
					}, this.trustTm * 1000);
					this.endOperateTime = moment().add(this.totalTick, 's');
				}
			}
		}

	}


	//玩家新增分数
	addUserBoomScore(uid) {

		if (this.boomType == 2 && this.curBoomUser && this.curBoomUser == uid) {
			let curBoomScore = this.boomScore * this.baseScore;
			let scoreTotal = 0;
			for (let key in this.userBoomScore) {
				if (key != uid) {
					this.userBoomScore[key] -= curBoomScore;
					scoreTotal += curBoomScore;
				}
			}
			this.userBoomScore[uid] += scoreTotal;
			this.curBoomUser = null;
			this.showLog(`[${uid}] user add boom score curBoomScore = ${curBoomScore}`);
		}
	}

	//用户操作
	onUserOperate(uid, data, autoUser = false) {

		//如果玩家操作了
		if (autoUser) {
			if (this.autoUser[uid]) {
				delete this.autoUser[uid];
				this.sendGameData(gameProto.gameTrushPush(uid, false), [uid]);
			}
		}

		if (this.autoUserTimer[uid]) {
			clearTimeout(this.autoUserTimer[uid]);
			delete this.autoUserTimer[uid];
		}

		if (uid != this.curOperateUid || this.gameStatus != gameProto.gameStatus.USER_OPERATE) {
			this.showLog(`can not operate ${uid} != ${this.curOperateUid} gameStatus = ${this.gameStatus}`, true);
			return;
		}

		this.showLog(`[${uid}] user operate  data = ${JSON.stringify(data)}`);
		this.clearOperateTimer();

		//2:判断玩家是否有炸弹分 炸弹管炸弹分
		this.addUserBoomScore(uid);

		let operateType = data.operateType;
		let cards = data.cards || [];

		let cardType = this.gameLogic.getCardType(cards, data.isShuaiWei);
		let operateInfo = { uid, operateType, cards, cardType, isSuccess: true, errMsg: null };

		if (cards.length > 0) {

			if (!this.isInCardList(cards, this.handCards[uid])) {
				this.showLog(`can not operate cards = ${cards} handCards = ${this.handCards[uid]}`, true);
				operateInfo.isSuccess = false;
				operateInfo.errMsg = '手上没有此牌';
				this.sendGameData(gameProto.gameUserOperatePush(uid, operateInfo), [uid]);
				return;
			}
		}

		//第一个玩家不能不出
		if (!this.lastOperateCards && operateType == gameProto.operateType.PASS) {
			operateInfo.isSuccess = false;
			operateInfo.errMsg = '首家必须出牌';
			this.sendGameData(gameProto.gameUserOperatePush(uid, operateInfo), [uid]);
			return;
		}

		//如果首轮有黑桃3 必出黑桃三
		// if (this.curBureau == 1 && this.heitao3User && this.heitao3 && !cards.includes(this.heitao3Code)) {
		// 	operateInfo.isSuccess = false;
		// 	operateInfo.errMsg = '首轮必出黑桃3';
		// 	await this.sendGameData(gameProto.gameUserOperatePush(uid, operateInfo), [uid]);
		// 	return;
		// }

		//判断拆炸弹没有
		if (!this.chaizhadan && this.gameLogic.isChaiZhaDan(cards, this.handCards[uid])) {

			operateInfo.isSuccess = false;
			operateInfo.errMsg = '炸弹不能拆分';
			this.sendGameData(gameProto.gameUserOperatePush(uid, operateInfo), [uid]);
			return;
		}

		//获取下一个玩家
		let nextUid = this.getNextUserID(uid);
		//如有下家报单 并且是单牌
		if (cardType == gameProto.cardType.SINGLE && this.baoDanUser.includes(nextUid)) {
			if (this.handCards[uid].find(item => this.gameLogic.getCardLogicValue(item) > this.gameLogic.getCardLogicValue(cards[0]))) {

				if (this.baoDanBiDa) {
					operateInfo.isSuccess = false;
					operateInfo.errMsg = "玩家报单,只能出最大的单牌";
					this.sendGameData(gameProto.gameUserOperatePush(uid, operateInfo), [uid]);
					return;
				} else {
					//如果不是最大的 如果报单玩家赢了 就要陪钱
					this.baoDanPeiUser = uid;
				}
			}
		} else {
			if (!this.baoDanUser.includes(uid)) {
				//报单的玩家出了牌要赔
				this.baoDanPeiUser = null;
			}
		}
		//记录用户的操作
		if (operateType == gameProto.operateType.PASS) {

			//操作记录
			let operateData = { cards: cards, uid: uid, type: operateType };
			this.operateRecords.push(operateData);

			this.curOperateUid = this.getNextUserID(uid);
			//全部都过了
			if (this.curOperateUid == this.lastOperateUid) {
				this.lastOperateCards = null;
			}
			//发送过牌操作
			operateInfo.handCards = this.handCards;
			this.sendGameData(gameProto.gameUserOperatePush(uid, operateInfo));
			//开始下一个玩家
			this.startUserOperate(this.curOperateUid);
			return;


		} else if (operateType == gameProto.operateType.OUT) {

			//判断出牌是否符合规则
			let result = this.gameLogic.compareCard(cards, this.lastOperateCards, data.isShuaiWei);
			operateInfo.isSuccess = result.isSuccess;
			operateInfo.errMsg = result.errMsg;
			if (operateInfo.isSuccess) {
				//清除用户手牌
				this.handCards[uid] = this.handCards[uid].filter(item => !cards.includes(item));
				//玩家是否报双和报单
				if (this.handCards[uid].length == 1) {
					//报单玩家
					this.baoDanUser.push(uid);
				}
				operateInfo.cards = this.gameLogic.getSortCard(cards.slice(), this.gameRule);
				//推送玩家手牌

				let curDatas = [];
				for (let key in this.handCards) {
					operateInfo.handCards = this.getHandCardsByUid(key);
					operateInfo.leftCards = this.getAllLeftCards(key);
					curDatas.push({ key, operateInfo: this.cloneObject(operateInfo) });
				}

				async.mapSeries(curDatas, async (curData, cb) => {
					let key = curData.key;
					let operateInfo = curData.operateInfo;
					await this.sendGameData(gameProto.gameUserOperatePush(uid, operateInfo), [key]);
					cb();
				}, (err, result) => {
					this.addUserOperate(uid, cards, operateType);
				});
			} else {
				//不符合规则
				this.sendGameData(gameProto.gameUserOperatePush(uid, operateInfo), [uid]);
				return;
			}
		}

	}


	//获取剩余卡牌
	getAllLeftCards(uid) {

		let allCards = [];
		for (let key in this.handCards) {

			if (this.handCards[key] && this.handCards[key].length > 0) {
				if (uid != key) {
					allCards = allCards.concat(this.handCards[key]);
				}
			}
		}
		if (this.leftCards) {
			allCards = allCards.concat(this.leftCards);
		}
		//获取卡牌及数量
		let cardResult = this.gameLogic.getCardsAndCount(allCards);
		let allCardResult = this.gameLogic.getCardsAndCount(this.gameLogic.getAllCards());
		let curList = [];
		for (let i = 0; i < allCardResult.countList.length; i++) {

			let card = parseInt(allCardResult.countList[i].card);
			let curCardCount = cardResult.cardCount[card];
			let count = curCardCount ? curCardCount : 0;
			curList.push({ card, count });
		}

		return curList;

	}

	//用户出牌记录
	addUserOperate(uid, cards, operateType) {

		//判断有炸弹没有
		let cardType = this.gameLogic.getCardType(cards.slice(), this.gameRule);
		//保存记录
		let data = { cards, uid, type: operateType, cardType, handCards: this.handCards[uid].slice() };
		this.operateRecords.push(data);

		let curBoomScore = 0;
		if (cardType == gameProto.cardType.BOMB_CARD) {
			curBoomScore = this.boomScore * this.baseScore;
			this.curBoomUser = uid;
		} else if (cardType == gameProto.cardType.TIAN_BOMB) {
			curBoomScore = this.boomScore * this.baseScore;
			this.curBoomUser = uid;
		}

		//如果有炸弹(空炸规则)
		if (this.boomType == 1 && curBoomScore) {
			let scoreTotal = 0;
			for (let key in this.userBoomScore) {
				if (key != uid) {
					this.userBoomScore[key] -= curBoomScore;
					scoreTotal += curBoomScore;
				}
			}
			this.userBoomScore[uid] += scoreTotal;
		}

		//最后出牌的玩家
		this.lastOperateUid = uid;
		this.lastOperateCards = cards;
		//检测游戏是否结束
		this.checkIsGameEnd(uid);

	}




	//判断游戏是否结束
	checkIsGameEnd(uid) {

		let curCount = this.handCards[uid].length;
		let isQiangEnd = this.isQiangGuan && this.qiangGuanUser && uid != this.qiangGuanUser;
		if (curCount == 0 || isQiangEnd) {
			this.addUserBoomScore(uid);
			//游戏结束
			setTimeout(() => { this.startResult(uid); }, 800);
		} else {
			//下一个玩家操作
			this.curOperateUid = this.getNextUserID(uid);
			this.startUserOperate(this.curOperateUid);
		}
	}


	//清除定时器
	clearOperateTimer() {
		if (this.operateTimer) {
			clearTimeout(this.operateTimer);
			this.operateTimer = null;
		}
	}

	//用户默认操作
	userAutoOperate(uid) {

		let user = this.getUserByUID(uid);
		if (user && user.playStatus == roomProto.userPlayEnum.PLAYING) {

			if (this.gameStatus == gameProto.gameStatus.NONE) {
				//等待状态默认准备操作
				this.userReady(user.userInfo.uid);
			}
			else if (this.gameStatus == gameProto.gameStatus.USER_QIANG) {
				//默认不抢
				this.onUserQiangGuan(user.userInfo.uid, { isQiang: false });
			}
			else if (this.gameStatus == gameProto.gameStatus.USER_DOUBLE) {
				//默认不加倍
				this.onUserDouble(user.userInfo.uid, { isDouble: false });
			}
			else if (this.gameStatus == gameProto.gameStatus.USER_OPERATE) {

				//目前默认不操作
				// this.onGameUserOperate(uid, { operateType: gameProto.operateType.CANCEL });
				if (user && this.curOperateUid == uid) {
					let curCards = this.lastOperateUid != uid ? this.lastOperateCards : null;
					let nextUid = this.getNextUserID(uid);
					let isBaoDan = this.handCards[nextUid].length == 1;
					//let resultCard = this.gameLogic.getAutoCard(this.handCards[uid], curCards, isBaoDan, this.curBureau);
					let userList = this.getPlayingUserList();
					let linuserList = [];
					let isRobot = false;
					for (let i = 0; i < userList.length; i++) {
						let user = this.getUserByUID(userList[i]);
						if (user) {
							if (!user.userInfo.isRobot) {
								isRobot = true;
							}
							linuserList.push({ uid: userList[i], chairID: user.chairID, })
						}
					}
					linuserList.sort((a, b) => { return a.chairID - b.chairID });
					let resultCard = [];
					//当前玩家是机器人 且有真人存在调用算法 
					if (user.userInfo.isRobot && isRobot) {
						resultCard = this.aiLogic.searchOutCard(this.lastOperateUid, this.curOperateUid, this.handCards, this.lastOperateCards, linuserList);
					} else {
						resultCard = this.gameLogic.getAutoCard(this.handCards[uid], curCards, isBaoDan);
					}
					if (!resultCard) {
						this.onUserOperate(uid, {
							operateType: gameProto.operateType.PASS,
							cards: null
						});
					} else {
						this.onUserOperate(uid, {
							operateType: gameProto.operateType.OUT,
							cards: resultCard
						});
					}

				}
			}
		}
	};

	//牌面回顾
	async onGameReview(uid) {
		await this.sendGameData(gameProto.gameReviewPush(this.reviewRecord), [uid]);
	};



	//开始计算结果
	startResult(uid) {

		this.clearOperateTimer();

		//摆牌结束 玩家结算
		this.gameStatus = gameProto.gameStatus.RESULT;
		this.totalTick = gameProto.gameStatusTm.RESULT;
		this.sendGameStatus(this.gameStatus, this.totalTick);
		let userList = this.getPlayingUserList();

		//计算分数并保存
		this.addResultRecord(uid, userList);
		//当前游戏结束
		this.gameEnd();

	};

	//计算分数并保存结果
	async addResultRecord(uid, userList) {

		//统计玩家分数
		let winScores = {};
		//玩家默认赢分为0
		winScores[uid] = 0;

		let hasChuntian = false;
		//关的状态
		let guanUserType = {};

		this.winCount[uid] = (this.winCount[uid] || 0) + 1;

		//如果有玩家抢关
		if (this.qiangGuanUser) {

			this.showLog(`[${this.qiangGuanUser}] user qiang guan result win uid = ${uid}  `);

			winScores[this.qiangGuanUser] = 0;
			for (let i = 0; i < userList.length; ++i) {
				let curUid = userList[i];
				if (curUid == this.qiangGuanUser) continue;
				let cardScore = this.cardCount * 4 * this.baseScore;
				//分数加倍
				if (this.isDouble) {
					cardScore *= (this.doubleUser[curUid] ? 2 : 1) * (this.doubleUser[this.qiangGuanUser] ? 2 : 1);
				}
				//加上炸弹分
				cardScore += -(this.userBoomScore[curUid] || 0);
				//抢关玩家赢了
				if (uid == this.qiangGuanUser) {
					winScores[curUid] = -cardScore; //枪关4倍
					winScores[this.qiangGuanUser] += cardScore; //输分
				} else {
					winScores[curUid] = cardScore; //枪关4倍
					winScores[this.qiangGuanUser] -= cardScore; //输分
				}
			}

		} else {

			let userList = this.getPlayingUserList();

			//判断其他玩家出牌没有
			if (!(this.operateRecords.some(item => item.uid != uid && item.type == gameProto.operateType.OUT))) {
				hasChuntian = true;
				//单双关 被关
				if (this.chairCount == 2) {
					guanUserType[uid] = gameProto.guanType.DAN;
					this.danGuanCount[uid] = (this.danGuanCount[uid] || 0) + 1;
				} else {
					guanUserType[uid] = gameProto.guanType.SHUANG;
					this.shuangGuanCount[uid] = (this.shuangGuanCount[uid] || 0) + 1;
				}

				let curList = userList.filter(item => item != uid);
				for (let i = 0; i < curList.length; i++) {
					let curUid = curList[i];
					guanUserType[curUid] = gameProto.guanType.BEI;
					this.beiGuanCount[uid] = (this.beiGuanCount[uid] || 0) + 1;
				}
			}

			//判断反春
			if (!hasChuntian && this.isFanGuan) {
				let bankerOut = this.operateRecords.filter(item => item.uid == this.bankerUid && item.type == gameProto.operateType.OUT);
				if (bankerOut.length == 1) {
					//反春庄家肯定是反关
					hasChuntian = true;
					guanUserType[this.bankerUid] = gameProto.guanType.FAN;
					this.fanGuanCount[this.bankerUid] = (this.fanGuanCount[this.bankerUid] || 0) + 1;
					if (this.chairCount == 3) {
						let otherUid = userList.find(item => item != uid && item != this.bankerUid);
						let otherOut = this.operateRecords.filter(item => item.uid == otherUid && item.type == gameProto.operateType.OUT);
						if (otherOut.length > 0) {
							//玩家有出牌
							guanUserType[uid] = gameProto.guanType.DAN;
							this.danGuanCount[uid] = (this.danGuanCount[uid] || 0) + 1;
						} else {
							guanUserType[otherUid] = gameProto.guanType.BEI;
							this.beiGuanCount[otherUid] = (this.beiGuanCount[otherUid] || 0) + 1;
							guanUserType[uid] = gameProto.guanType.SHUANG;
							this.shuangGuanCount[uid] = (this.shuangGuanCount[uid] || 0) + 1;

						}
					} else {
						//两个人只有反关和单关
						guanUserType[uid] = gameProto.guanType.DAN;
						this.danGuanCount[uid] = (this.danGuanCount[uid] || 0) + 1;
					}
				}
			}

			for (let i = 0; i < userList.length; ++i) {
				let curUid = userList[i];
				if (curUid != uid) {
					winScores[curUid] = 0;
					//计算手牌的分数
					let cardsCount = this.handCards[curUid].length;
					let cardScore = cardsCount * this.baseScore;
					//春天翻倍
					if (hasChuntian) {
						cardScore *= 2;
					}
					//十张以上1.5倍
					// if (this.last10 && cardsCount >= 10) {
					// 	cardScore *= 1.5;
					// }
					//红桃10翻倍
					if (this.hongtao10 && (this.hongtao10User == uid || this.hongtao10User == curUid)) {
						cardScore *= 2;
					}
					//报单不输分
					if (cardsCount == 1) {
						cardScore = 0;
					}

					//分数加倍
					if (this.isDouble) {
						cardScore *= (this.doubleUser[uid] ? 2 : 1) * (this.doubleUser[curUid] ? 2 : 1);
					}
					//加上炸弹分
					cardScore += -(this.userBoomScore[curUid] || 0);

					if (this.chairCount >= 3 && this.baoDanPeiUser && this.baoDanPeiUser != curUid) {
						winScores[this.baoDanPeiUser] += -cardScore;
						this.showLog(`[${this.baoDanPeiUser}] user pei score curUid = ${curUid} score = ${cardScore} `);
					} else {
						winScores[curUid] += -cardScore;
					}
					winScores[uid] += cardScore;
				}
			}
		}

		let resultInfo = [];
		//输赢分加上炸弹分为总分
		for (let key in winScores) {
			//	winScores[key] += this.userBoomScore[key] || 0;
			if (!this.userWinTotalScore[key]) {
				this.userWinTotalScore[key] = 0;
			}
			this.userWinTotalScore[key] += winScores[key];

			//统计每一局的输赢
			if (!this.winList[key]) {
				this.winList[key] = [];
			}
			this.winList[key].push(winScores[key]);

			//组合结算数据
			let user = this.getUserByUID(key);
			if (user) {

				let result = {
					uid: key,
					endTime: Date.now(),
					nickname: user.userInfo.nickname,
					avatar: user.userInfo.avatar,
					chairID: user.chairID,
					userScore: user.userInfo.score,
					winScores: winScores[key],
					userCards: this.handCards[key],
					userBoomScore: this.userBoomScore[key],
					beginCards: this.cloneHandCards[key],
				};
				resultInfo.push(result);

				//总输赢记录
				if (!this.userWinRecord[key]) {
					this.userWinRecord[key] = {
						uid: key,
						nickname: user.userInfo.nickname,
						avatar: user.userInfo.avatar,
						score: 0,
						winScore: 0,
					};
				}
				this.userWinRecord[key].score += winScores[key];
				this.userWinRecord[key].winScore += winScores[key];
			}
		}
		this.result = winScores;

		//this.showLog(`send game result ${JSON.stringify(resultInfo)}`);

		let plusInfo = { baoDanPeiUser: this.baoDanPeiUser, guanUserType, qiangUserOperate: this.qiangUserOperate, qiangGuanUser: this.qiangGuanUser, doubleUser: this.doubleUser };
		let message = gameProto.gameResultPush(resultInfo, hasChuntian, this.leftCards, plusInfo);
		await this.sendGameData(message);


		this.reviewRecord.push(message.data);
		//游戏视频回放
		let detail = utils.clone(message.data);
		detail.operateRecords = this.operateRecords;
		detail.endTime = Date.now();
		this.gameVideoRecord.push(detail);

		//庄家判断
		this.bankerUid = (this.outCardSort == gameProto.outSortType.TURN) ? this.getNextUserID(this.bankerUid) : uid;
		//结算用户分数并更新
		let endData = [];
		for (let key in winScores) {
			endData.push({ uid: key, score: winScores[key] });
		}

		await this.recordGameResult(endData).catch((err) => { logger.error(err.stack); });



	}


	//游戏结束
	async gameEnd() {

		await this.concludeGame();
		//游戏未解散 开始下一局
		if (!this.roomDismissed) {
			//this.resetGame();
			this.gameStatus = gameProto.gameStatus.NONE;
			this.resetPlayerStatus();
			this.totalTick = gameProto.gameStatusTm.RESULT;
			this.sendGameStatus(this.gameStatus, this.totalTick);

			// this.operateTimer = setTimeout(() => {
			// 	//玩家自动准备开始
			// 	if (this.gameStatus == gameProto.gameStatus.NONE) {
			// 		let userList = this.getWaitingUserList();
			// 		userList.forEach(uid => {
			// 			this.userReady(uid);
			// 		});
			// 	}
			// }, this.totalTick * 1000)
		}
	}

	//房间解散 弹出总结算消息
	async onEventRoomDismiss(reason) {

		this.clearOperateTimer();

		let result = [];
		for (let key in this.userWinRecord) {
			let data = this.userWinRecord[key];
			result.push({
				uid: data.uid,
				nickname: data.nickname,
				avatar: data.avatar,
				score: data.score,
				winList: this.winList[data.uid],
				yingPai: this.winCount[key] || 0,
				danGuan: this.danGuanCount[key] || 0,
				shuangGuan: this.shuangGuanCount[key] || 0,
				beiGuan: this.beiGuanCount[key] || 0,
				fanGuan: this.fanGuanCount[key] || 0,
			});
		}

		let winMost = null;
		let loseMost = null;
		if (result.length > 0) {
			winMost = 0;
			loseMost = 0;
			for (let i = 1; i < result.length; ++i) {
				if (result[i].score > result[winMost].score) {
					winMost = i;
				}
				if (result[i].score < result[loseMost].score) {
					loseMost = i;
				}
			}
			winMost = result[winMost].uid;
			loseMost = result[loseMost].uid;
		}

		let creatorInfo = null;
		let creator = this.getUserByUID(this.creatorInfo.uid);
		if (creator) {
			creatorInfo = {
				uid: creator.userInfo.uid,
				nickname: creator.userInfo.nickname,
				avatar: creator.userInfo.avatar,
			};
		}
		let plusInfo = { payDiamondUser: this.payDiamondUser };
		let message = gameProto.gameEndPush(result, winMost, loseMost, creatorInfo, plusInfo);
		await this.sendGameData(message);

	};


	/*================测试功能========================*/

	//玩家换牌
	async onChangeCard(uid, data) {

		let cardData = data.cardData;
		let curUid = data.uid;
		let user = this.getUserByUID(uid);
		if (user.userInfo.isTest) {
			//获取牌库
			let isSuccess = this.gameLogic.isInCardList(this.userNextTestCards, cardData);
			if (isSuccess) {
				this.userNextTestCards[curUid] = cardData;
				this.showLog(`[${uid}] set user = ${curUid} cards = ${JSON.stringify(cardData)} `);
			}
			await this.sendGameData(gameProto.gameSetTestCardsPush(isSuccess), [user.uid]);
		}
	}

	//查询牌库
	async onSearchCardsList(uid, data) {
		let user = this.getUserByUID(uid);
		if (user.userInfo.isTest) {
			//获取牌库
			let allCardData = this.gameLogic.getExceptCards(this.userNextTestCards);
			let curUid = data.uid;
			await this.sendGameData(gameProto.gameGetTestCardsPush(uid, allCardData, this.userNextTestCards[curUid]), [user.uid]);
		}
	}

}

module.exports = gameFrame;